package com.newtouch.bxzs.business.message.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.newtouch.bxzs.business.enclosure.mapper.AttachmentMapper;
import com.newtouch.bxzs.business.enclosure.service.AttachmentService;
import com.newtouch.bxzs.business.message.controller.AnnouncementController;
import com.newtouch.bxzs.business.message.mapper.AnnouncementMapper;
import com.newtouch.bxzs.business.message.mapper.SysParameterMapper;
import com.newtouch.bxzs.business.message.model.AnnouncementAddVO;
import com.newtouch.bxzs.business.message.model.AnnouncementBO;
import com.newtouch.bxzs.business.message.model.AnnouncementDetailBO;
import com.newtouch.bxzs.business.message.model.AnnouncementVO;
import com.newtouch.bxzs.business.message.service.AnnouncementService;
import com.newtouch.bxzs.common.base.BaseErrorEnum;
import com.newtouch.bxzs.common.base.BaseException;
import com.newtouch.bxzs.common.base.em.PublishStatusEnum;
import com.newtouch.bxzs.common.base.em.SystemParamsEnum;
import com.newtouch.bxzs.common.base.model.Announcement;
import com.newtouch.bxzs.common.base.model.Attachment;
import com.newtouch.bxzs.common.base.model.SystemParameter;
import com.newtouch.bxzs.common.base.model.vo.EnclosureVO;
import com.newtouch.bxzs.common.base.util.EntityToBeanUtils;
import com.newtouch.bxzs.common.base.util.RedisUtils;
import com.newtouch.bxzs.common.base.util.UserDetailUtil;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 公告管理service接口的实现类
 *
 * <p>
 * <b>History:</b>
 * <table border="1">
 * <tr>
 * <th>Date</th>
 * <th>Operator</th>
 * <th>Memo</th>
 * </tr>
 * <tr>
 * <td>2020/12/23</td>
 * <td>liman</td>
 * <td>Create</td>
 * </tr>
 * </table>
 *
 * @author liman
 * @version 1.0.0
 * @since 1.0.0
 */

@Service
public class AnnouncementServiceImpl implements AnnouncementService {

    @Resource
    private AnnouncementMapper announcementMapper;

    @Resource
    private AttachmentMapper attachmentMapper;

    @Autowired
    private AttachmentService attachmentService;

    @Resource
    private SysParameterMapper sysParameterMapper;

    @Autowired
    private RedisUtils redisUtils;

    /**
     * 已发布状态码常量
     */
    private static final String PUBLISHED_CODE = PublishStatusEnum.PUBLISHED_STATUS.getResultCode();

    /**
     * 撤销状态码常量
     */
    private static final String REVOKED_CODE = PublishStatusEnum.REVOKED_STATUS.getResultCode();

    /**
     * 未发布状态码常量
     */
    private static final String UNPUBLISH_CODE = PublishStatusEnum.UNPUBLISH_STATUS.getResultCode();

    private static final String ATTACHMENT_FORMAT = "pdf";

    /**
     * 静态变量：日志
     */
    private static final Log logger = LogFactory.getLog(AnnouncementController.class);

    /**
     * (non-Javadoc)
     *
     * @see com.newtouch.bxzs.business.message.service.AnnouncementService#getAnnouncementList(com.newtouch.bxzs.business.message.model.AnnouncementVO)
     */
    @Override
    public IPage<AnnouncementBO> getAnnouncementList(AnnouncementVO announcementVO) {
        /* 判断页码是否为空 */
        if (announcementVO == null || StringUtils.isEmpty(announcementVO.getCurrentPage())
                || StringUtils.isEmpty(announcementVO.getPageSize())) {
            logger.error("页码输入为空");
            throw new BaseException(BaseErrorEnum.REQUEST_DATA_NULL);
        }
        /* 拿到当前页和每页数量 */
        Page<Announcement> announcementPage = new Page<>(announcementVO.getCurrentPage(), announcementVO.getPageSize());
        // 实例化QueryWrapper
        QueryWrapper<Announcement> query = new QueryWrapper<>();
        /* 查询已发布的公告 APP端 */
        if (PUBLISHED_CODE.equals(announcementVO.getPublishStatus())) {
            query.eq("PUBLISH_STATUS", PUBLISHED_CODE);
            /* 选择排序顺序（按照发布时间降序排列） */
            query.orderByDesc("PUBLISH_TIME");
        }
        /* 查询全部公告 PC端 */
        else {
            if (announcementVO.getStartTime() != null || announcementVO.getEndTime() != null) {
                if (announcementVO.getStartTime() != null && announcementVO.getEndTime() != null &&
                        announcementVO.getStartTime().after(announcementVO.getEndTime())) {
                    logger.error("开始时间不能大于结束时间");
                    throw new BaseException(BaseErrorEnum.REQUEST_PARAMETER_ERROR);
                }
                /* 根据开始时间查询 */
                if (announcementVO.getStartTime() != null) {
                    query.ge("PUBLISH_TIME", announcementVO.getStartTime());
                }
                /* 根据结束时间查询 */
                if (announcementVO.getEndTime() != null) {
                    query.le("PUBLISH_TIME", announcementVO.getEndTime());
                }
                /* 选择排序顺序（按照发布时间降序排列） */
                query.orderByDesc("PUBLISH_TIME");
            } else {
                /* 选择排序顺序（按照修改时间降序排列） */
                query.orderByDesc("UPDATE_TIME");
            }
        }
        /* 通过输入条件查询 */
        IPage<Announcement> iPage = announcementMapper.selectPage(announcementPage, query);
        if (iPage == null || CollectionUtil.isEmpty(iPage.getRecords())) {
            return null;
        }
        List<AnnouncementBO> boList = EntityToBeanUtils.copyBean(iPage.getRecords(), AnnouncementBO.class);
        /* 根据公告id集合查询附件集合 */
        QueryWrapper<Attachment> query2 = new QueryWrapper<>();
        query2.in("ATTACHMENT_RELATION_ID",
                iPage.getRecords().stream().map(Announcement::getAnnouncementId).collect(Collectors.toList()));
        List<Attachment> attachmentList = attachmentMapper.selectList(query2);
        if (!CollectionUtil.isEmpty(attachmentList)) {
            for (AnnouncementBO announcementBO : boList) {
                //是否获取url标志
                boolean flag = false;
                for (Attachment attachment : attachmentList) {
                    if (Objects.equals(attachment.getAttachmentRelationId(), announcementBO.getAnnouncementId())) {
                        announcementBO.setAttachmentUrls(attachment.getAttachmentUrl() + "/"
                                + attachment.getAttachmentName());
                        if (!ATTACHMENT_FORMAT.equals(attachment.getAttachmentName().split("\\.")[attachment.getAttachmentName().split("\\.").length-1])){
                            flag = true;
                        }
                    }
                    if (flag) {
                        break;
                    }
                }
            }
        }
        IPage<AnnouncementBO> boiPage = new Page<>();
        // 设置iPage*/
        boiPage.setRecords(boList);
        boiPage.setTotal(iPage.getTotal());
        boiPage.setSize(iPage.getSize());
        boiPage.setPages(iPage.getPages());
        boiPage.setCurrent(iPage.getCurrent());
        return boiPage;
    }

    /**
     * (none-Javadoc)
     *
     * @see com.newtouch.bxzs.business.message.service.AnnouncementService#
     * getAnnouncementDetail(java.lang.String)
     */
    @Override
    public AnnouncementDetailBO getAnnouncementDetail(String announcementId) {
        if (StringUtils.isEmpty(announcementId)) {
            logger.error("公告id输入为空");
            throw new BaseException(BaseErrorEnum.REQUEST_DATA_NULL);
        }
        /* 通过id查询公告 */
        Announcement announcement = announcementMapper.selectById(announcementId);
        if (announcement == null) {
            logger.error("公告id输入错误");
            return null;
        }
        /* 实例化AnnouncementBO8 */
        AnnouncementDetailBO announcementDetailBO = new AnnouncementDetailBO();
        /* 使用工具直接进行实体与BO的复制转换，前面是来源，后面是目标 */
        BeanUtil.copyProperties(announcement, announcementDetailBO);
        /* 获取附件 */
        HashMap<String, List<String>> result = attachmentService
                .getEnclosureList(new EnclosureVO(announcementId, null));
        if (result != null) {
            announcementDetailBO.setAttachmentUrls(result);
        }
        return announcementDetailBO;
    }

    /**
     * (non-Javadoc)
     *
     * @see com.newtouch.bxzs.business.message.service.AnnouncementService#deleteAnnouncement(java.util.List)
     */
    @Override
    public String deleteAnnouncement(List<String> announcementIds) {
        if (CollectionUtil.isEmpty(announcementIds)) {
            logger.info("公告id集合输入为空");
            throw new BaseException(BaseErrorEnum.REQUEST_DATA_NULL);
        }
        // 实例化QueryWrapper
        QueryWrapper<Announcement> query = new QueryWrapper<>();
        query.in("ANNOUNCEMENT_ID", announcementIds);
        List<Announcement> list = announcementMapper.selectList(query);
        if (CollectionUtil.isEmpty(list)) {
            throw new BaseException(BaseErrorEnum.REQUEST_PARAMETER_ERROR);
        }
        for (Announcement announcement : list) {
            /* 判断公告ID是否输错，不能删除已发布*/
            if (list.size() == 0 || announcement == null || PUBLISHED_CODE.equals(announcement.getPublishStatus())) {
                logger.info("公告id集合输入异常");
                throw new BaseException(BaseErrorEnum.PUBLISH_STATUS);
            }
        }
        /* 设置修改人状态和修改时间 */
        QueryWrapper<Announcement> updateQuery = new QueryWrapper<>();
        updateQuery.in("ANNOUNCEMENT_ID", announcementIds);
        Announcement announcement = new Announcement();
        announcement.setUpdateTime(new Date());
        announcement.setUpdateUserId(Objects.requireNonNull(UserDetailUtil.getUserDetail()).getUserId());
        announcementMapper.update(announcement, updateQuery);
        /* 批量逻辑删除公告 */
        if (announcementMapper.deleteBatchIds(announcementIds) > 0) {
            /* 批量删除附件信息 */
            attachmentMapper.delete(new QueryWrapper<Attachment>().in("ATTACHMENT_RELATION_ID", announcementIds));
        }
        return "批量逻辑删除成功";
    }

    /**
     * (non-Javadoc)
     *
     * @see com.newtouch.bxzs.business.message.service.AnnouncementService#changeAnnouncementStatus(java.lang.String, java.lang.String)
     */
    @Override
    public String changeAnnouncementStatus(String announcementId, String announcementStatus) {
        /*
         * 判断announcementId是否为空 || 判断announcementStatus是否为空 || 判断只能修改状态为发布和撤销
         */
        boolean conditions = StringUtils.isEmpty(announcementId) || StringUtils.isEmpty(announcementStatus) || (!PUBLISHED_CODE.equals(announcementStatus) && !REVOKED_CODE.equals(announcementStatus));
        if (conditions) {
            logger.error("修改状态输入异常");
            throw new BaseException(BaseErrorEnum.REQUEST_DATA_NULL);
        }
        /* 实例化QueryWrapper */
        QueryWrapper<Announcement> query = new QueryWrapper<>();
        /* 实例化Announcement */
        Announcement announcement = new Announcement();
        /* 设置announcementId为sql条件 */
        query.eq("ANNOUNCEMENT_ID", announcementId);
        Date date = new Date();
        /* 通过公告id查询还未修改的公告 */
        Announcement announcementTwo = announcementMapper.selectById(announcementId);
        if (announcementTwo == null) {
            logger.error("公告id输入错误");
            throw new BaseException(BaseErrorEnum.REQUEST_PARAMETER_ERROR);
        }
        /* 如果为撤销，则设置撤销时间为当前时间 */
        if (REVOKED_CODE.equals(announcementStatus)) {
            announcement.setRevokeTime(date);
            /*
             * 由于公告实体，加上了可以修改时间为空的方法updateStrategy=FieldStrategy.IGNORED，
             * 所以这里必须设置发布时间，即拿到未修改之前的时间
             */
            announcement.setPublishTime(announcementTwo.getPublishTime());
        }
        /* 如果为发布，则设置发布时间为当前时间 */
        if (PUBLISHED_CODE.equals(announcementStatus)) {
            announcement.setPublishTime(date);
            /* 当点击发布时如果撤销时间小于当前时间，撤销时间设为空，大于则设置为修改之前的时间 */
            Date revokeTime = announcementTwo.getRevokeTime();
            if (revokeTime != null && revokeTime.after(date)) {
                announcement.setRevokeTime(revokeTime);
            }
        }
        /* 设置发布状态，修改人，和修改时间 */
        announcement.setPublishStatus(announcementStatus);
        announcement.setUpdateUserId(Objects.requireNonNull(UserDetailUtil.getUserDetail()).getUserId());
        announcement.setUpdateTime(date);
        /* 修改 */
        announcementMapper.update(announcement, query);
        return "修改公告发布状态成功";
    }

    /**
     * (non-Javadoc)
     *
     * @see com.newtouch.bxzs.business.message.service.AnnouncementService#modifyAnnouncement(com.newtouch.bxzs.business.message.model.AnnouncementAddVO)
     */
    @Override
    public String modifyAnnouncement(AnnouncementAddVO announcementVO) {
        if (announcementVO == null || StringUtils.isEmpty(announcementVO.getAnnouncementContent())
                || StringUtils.isEmpty(announcementVO.getAnnouncementIntroduction())
                || StringUtils.isEmpty(announcementVO.getAnnouncementName())
                || StringUtils.isEmpty(announcementVO.getPublishStatus())) {
            logger.error("输入必填项为空");
            throw new BaseException(BaseErrorEnum.REQUEST_DATA_NULL);
        }
        /* 实例化Announcement */
        Announcement announcement = new Announcement();
        /* 使用工具直接进行实体与BO的复制转换，前面是来源，后面是目标 */
        BeanUtil.copyProperties(announcementVO, announcement);
        Date date = new Date();
        //从缓存里面拿到公告最大发布时间限制，（天）
        String maxPublishDayLimit = getParameterValueRedis(SystemParamsEnum.MAX_PUBLISH_DAY_LIMIT.getCode());
        int maxPublishTime = Integer.parseInt(maxPublishDayLimit);
        //从缓存里面拿到公告最大撤销最大时间，（YYYY-MM-DD）
        String maxRevokeDateLimit = getParameterValueRedis(SystemParamsEnum.MAX_REVOKE_DATE_LIMIT.getCode());
        //将字符串YYYY-MM-DD,转换为时间
        DateTime dateTimeMax = DateUtil.parseDate(maxRevokeDateLimit);
        /* 当点击发布时 */
        if (PUBLISHED_CODE.equals(announcement.getPublishStatus())) {
            /* 发布时间小于当前时间，则立即发布，设置发布时间为当前时间 */
            announcement.setPublishTime(date);
        } else {
            /* 距离当前时间七天 */
            DateTime dateTimePublish = DateUtil.offsetDay(date, maxPublishTime);
            /* 发布时间 */
            Date publishTime = announcement.getPublishTime();
            /* 撤销时间 */
            Date revokeTime = announcement.getRevokeTime();
            /*
             * 判断发布时间是否在七天内 || 撤销时间必须晚于发布时间 || 撤销时间不能小于当前时间 ||
             * 撤销时间不能大于2050-01-01
             */
            boolean exits = publishTime != null && ((publishTime.after(dateTimePublish) || publishTime.before(date))
                    || (revokeTime != null && revokeTime.before(publishTime)));
            if (exits) {
                logger.error("时间输入异常");
                throw new BaseException("时间输入异常");
            }
        }
        boolean condition = announcement.getRevokeTime() != null
                && (announcement.getRevokeTime().before(date) || announcement.getRevokeTime().after(dateTimeMax));
        if (condition) {
            logger.error("撤销时间输入异常");
            throw new BaseException("撤销时间输入异常");
        }
        /* 如果announcementId为空，新增公告 */
        if (StringUtils.isEmpty(announcement.getAnnouncementId())) {
            announcement.setCreateUserId(Objects.requireNonNull(UserDetailUtil.getUserDetail()).getUserId());
            announcement.setCreateTime(date);
            announcement.setUpdateUserId(Objects.requireNonNull(UserDetailUtil.getUserDetail()).getUserId());
            announcement.setUpdateTime(date);
            announcement.setDeleteFlag(false);
            announcementMapper.insert(announcement);
        } else {
            announcement.setUpdateUserId(Objects.requireNonNull(UserDetailUtil.getUserDetail()).getUserId());
            announcement.setUpdateTime(date);
            /* 如果announcementId不为空，修改公告 */
            announcementMapper.updateById(announcement);
        }
        return announcement.getAnnouncementId();
    }

    /**
     * 根据系统参数编码,获取缓存,如果为空更新缓存
     *
     * @param code 系统参数编码
     * @return 系统参数值
     */
    @Override
    public String getParameterValueRedis(String code) {
        if (StringUtils.isEmpty(code)) {
            throw new BaseException(BaseErrorEnum.REQUEST_DATA_NULL);
        }
        String systemParameterValue = redisUtils.get(code);
        //如果值为空，更新缓存
        if (StringUtils.isEmpty(systemParameterValue)) {
            QueryWrapper<SystemParameter> query = new QueryWrapper<>();
            query.eq("SYSTEM_PARAMETER_CODE", code);
            query.eq("IS_ENABLE", true);
            SystemParameter systemParameter = sysParameterMapper.selectOne(query);
            systemParameterValue = systemParameter.getSystemParameterValue();
            if (StringUtils.isEmpty(systemParameterValue)) {
                throw new BaseException(BaseErrorEnum.NOT_FOUND);
            }
            //更新缓存
            redisUtils.getAndSet(code, systemParameterValue);
        }
        return systemParameterValue;
    }

}